/* 
 * Copyright 2011 kOSMik.
 * This file is part of kOSMik.
 * 
 * kOSMik is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * kOSMik is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with kOSMik.  If not, see <http://www.gnu.org/licenses/>.
 *  
 * kOSMik:
 * Open Street Map - Editor.
 * http://code.google.com/p/swp-dv-ws2010-osm-2/
 */

package de.fuberlin.kosmik.data.osmt;

import java.util.ArrayList;
import java.util.List;

/**
 * This class encapsulate a bundles_to_features relation of bundles_to_features
 * table.
 * 
 * @author Johannes
 * 
 */
public class OsmtBundle {

	private final OsmtData osmtData;
	private OsmtNode osmtNode;
	private final ArrayList<OsmtFeatureRecord> featureList = new ArrayList<OsmtFeatureRecord>();
	private OsmtNodeRecord record = new OsmtNodeRecord();

	/**
	 * Loads a bundle of features from database, if it exists.
	 * 
	 * @param osmtData The database
	 * @param bundleId The id of the bundle
	 * @throws OsmtObjectException if no such node id named in the database.
	 */
	public OsmtBundle(final OsmtData osmtData, final Integer bundleId)
			throws OsmtObjectException {
		super();
		this.osmtData = osmtData;
		if (!loadBundleById(this, bundleId.intValue())) {
			throw new OsmtObjectException(String.format("can not load Bundle with id %d.",
					bundleId));
		}
	}

	/**
	 * Constructor.
	 * <p>
	 * Use this constructor to work on a bundle that does not exists in the
	 * database.
	 * 
	 * @param osmtData The database for read and insert features.
	 * @param name The name to display.
	 */
	public OsmtBundle(final OsmtData osmtData, String name) {
		super();
		this.osmtData = osmtData;
		this.record.displayName = name;
	}

	private static boolean loadBundleById(final OsmtBundle osmtBundle, int bundleId) {
		try {
			osmtBundle.osmtNode = new OsmtNode(osmtBundle.osmtData, bundleId, null, null, null,
					null, null, null, null, null);
		} catch (OsmtObjectException e) {
			return false;
		}
		osmtBundle.record = osmtBundle.osmtNode.get();

		// check, if the given node id identify a bundle?
		if (!osmtBundle.record.isBundle)
			return false;

		fetchAllFeaturesForBundle(osmtBundle);
		return osmtBundle.featureList.size() > 0;
	}

	private static void fetchAllFeaturesForBundle(OsmtBundle osmtBundle) {
		osmtBundle.featureList.clear();
		osmtBundle.featureList.addAll(osmtBundle.osmtData
				.fetchAllFeaturesOfBundleWithId(osmtBundle.getId()));

		// ensure database consistency
		for (OsmtFeatureRecord featureRecord : osmtBundle.featureList) {
			if (featureRecord.elements == 0) {
				// maybe, the features itself are build up by a join and does
				// not exists itself in table Feature
				// Therefore force insertion.
				try {
					final OsmtFeature notNeededObject = new OsmtFeature(osmtBundle.osmtData,
							featureRecord);
					if (notNeededObject.get().elements != 0) {
						// success, feature inserted
					}
				} catch (OsmtObjectException e) {
					// here nothing to do, react later
				}
			}
		}
	}

	/**
	 * Get all features which are related with this bundle.
	 * 
	 * @return a {@link List} with all features.
	 */
	public List<OsmtFeatureRecord> getAllFeatures() {
		return featureList;
	}

	/**
	 * Get a feature at the specified index position.
	 * 
	 * @param index of the feature.
	 * @return the feature, if exists
	 */
	public OsmtFeatureRecord getFeatureAtIndex(final int index) {
		return featureList.get(index);
	}

	/**
	 * Get the count of features related to this bundle.
	 * 
	 * @return the count.
	 */
	public int countOfFeatures() {
		return featureList.size();
	}

	/**
	 * 
	 * @return the id of the bundle.
	 */
	public int getId() {
		return record.id;
	}

	/**
	 * Get all data like name and comment for this node.
	 * 
	 * @return a OsmtNodeRecord containing all bundle data.
	 */
	public OsmtNodeRecord get() {
		return record;
	}

	/**
	 * Removes one instance of the specified feature from this List if one is
	 * contained. This method delegates to {@link ArrayList#remove(Object)}.
	 * 
	 * Modifications by call this method does not have any effect to database.
	 * 
	 * If the list only have one element, so this elements is not removable.
	 * 
	 * @param feature that should remove
	 * @return true, if the list was modified, otherwise false.
	 */
	public boolean delete(OsmtFeatureRecord feature) {
		if (featureList.size() > 1 && feature.modifiable)
			return featureList.remove(feature);
		return false;
	}

	/**
	 * Add the feature, if this feature does not yet exists in the list.
	 * 
	 * @param feature to add.
	 * @return true, if the feature was added successfully.
	 */
	public boolean add(OsmtFeatureRecord feature) {
		if (!featureList.contains(feature))
			return featureList.add(feature);
		return false;
	}

	/**
	 * Add the feature, if this feature does not yet exists in the list.
	 * 
	 * @param keyName First part of the PK that identify a feature.
	 * @param valueName Second part of the PK that identify a feature.
	 * 
	 * @return true, if the feature was added successfully.
	 */
	public boolean add(String keyName, String valueName) {
		final OsmtFeature feature;
		try {
			feature = new OsmtFeature(osmtData, keyName, valueName, null, null, null, null,
					null, null, null);
			if (feature.hasJustBeenInserted()) {
				// if the feature was not loaded but new created, set the
				// elements-attribut to fit all elements
				feature.set(keyName, valueName, Boolean.TRUE, Boolean.TRUE, Boolean.TRUE, "",
						"", "", "");
			}
		} catch (OsmtObjectException e) {
			return false;
		}

		final OsmtFeatureRecord featureRecord = feature.get();
		if (!featureList.contains(featureRecord))
			return featureList.add(featureRecord);
		return false;
	}

	/**
	 * Use this method to signalize the statistic, that this osm element was
	 * used by a user one more time.
	 * 
	 * @param bundle which use counter should be increment.
	 */
	public static void useOneMoreTime(final OsmtBundle bundle) {
		if (bundle != null && bundle.osmtNode != null)
			OsmtNode.useOneMoreTime(bundle.osmtNode);
	}

}
